package burubaka.common;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slim3.datastore.Datastore;
import org.slim3.datastore.ModelMeta;
import org.slim3.util.TimeZoneLocator;

import burubaka.BurubakaConstants;
import burubaka.model.RaceInfo;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.urlfetch.FetchOptions;
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;

/**
 * @author tk
 * 
 */
public class BurubakaUtil {

	// private static final Logger log =
	// Logger.getLogger(BurubakaUtil.class.getName());

	/**
	 * フェッチ
	 * 
	 * @param url
	 * @return HTTPResponse
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public static HTTPResponse fetch(String url) throws IOException, InterruptedException {
		return fetch(url, HTTPMethod.POST);
	}

	/**
	 * フェッチ
	 * 
	 * @param url
	 * @param method
	 * @return HTTPResponse
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public static HTTPResponse fetch(String url, HTTPMethod method) throws IOException, InterruptedException {
		FetchOptions fo = FetchOptions.Builder.disallowTruncate().setDeadline(10.0);
		HTTPRequest request = new HTTPRequest(new URL(url), method, fo);
		try {
			return URLFetchServiceFactory.getURLFetchService().fetch(request);
		} catch (IOException e) {
			Thread.sleep(600);
			return URLFetchServiceFactory.getURLFetchService().fetch(request);
		}
	}

	/**
	 * @param url
	 * @param method
	 * @return URLText
	 * @throws MalformedURLException
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public static String getURLText(String url, HTTPMethod method) throws MalformedURLException, IOException,
			InterruptedException {
		return getURLText(url, method, BurubakaConstants.ENCODE_SJIS);
	}

	/**
	 * @param url
	 * @param method
	 * @param encode
	 * @return URLText
	 * @throws MalformedURLException
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public static String getURLText(String url, HTTPMethod method, String encode) throws MalformedURLException,
			IOException, InterruptedException {
		byte[] cont = fetch(url, method).getContent();
		return new String(cont, encode);
	}

	/**
	 * @param url
	 * @return URLText
	 * @throws MalformedURLException
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public static String getURLText(String url) throws MalformedURLException, IOException, InterruptedException {
		return new String(fetch(url).getContent(), BurubakaConstants.ENCODE_SJIS);
	}

	/**
	 * @param strTag
	 * @param endTag
	 * @param bStr
	 * @return String
	 */
	public static String substring(String strTag, String endTag, String bStr) {

		int start = bStr.indexOf(strTag);
		if (StringUtils.isBlank(strTag)) {
			start = 0;
		}
		if (start < 0) {
			return null;
		}
		start = start + strTag.length();
		int end = bStr.indexOf(endTag, start);
		if (StringUtils.isBlank(endTag)) {
			end = bStr.length();
		}
		if (end < 0) {
			return null;
		}

		return bStr.substring(start, end).trim();
	}

	/**
	 * @param strTag
	 * @param endTag
	 * @param bStr
	 * @return String
	 */
	public static String substringFromLast(String strTag, String endTag, String bStr) {

		int start = bStr.lastIndexOf(strTag);
		if (start < 0) {
			return null;
		}
		start = start + strTag.length();
		int end = bStr.indexOf(endTag, start);
		if (end < 0) {
			return null;
		}

		return bStr.substring(start, end).trim();
	}

	/**
	 * @param strTag
	 * @param endTag
	 * @param bStr
	 * @return ArrayList
	 */
	public static ArrayList<String> substringList(String strTag, String endTag, String bStr) {

		ArrayList<String> ret = new ArrayList<String>();
		int point = 0;

		while (true) {
			int start = bStr.indexOf(strTag, point);
			if (start < 0) {
				break;
			}
			start = start + strTag.length();
			int end = bStr.indexOf(endTag, start);
			if (end < 0) {
				break;
			}
			ret.add(bStr.substring(start, end).trim());
			point = start;
		}
		return ret;
	}

	/**
	 * @param strTag
	 * @param endTag
	 * @param array
	 * @return ArrayList
	 */
	public static ArrayList<String> substringList(String strTag, String endTag, ArrayList<String> array) {

		ArrayList<String> ret = new ArrayList<String>();

		for (String bStr : array) {
			int start = bStr.indexOf(strTag);
			if (start < 0) {
				ret.add("");
				continue;
			}
			start = start + strTag.length();
			int end = bStr.indexOf(endTag, start);
			if (end < 0) {
				ret.add("");
				continue;
			}
			ret.add(bStr.substring(start, end).trim());
		}

		return ret;
	}

	/**
	 * @param list
	 * @return boolean
	 */
	public static boolean isEmpty(List<?> list) {
		return list == null || list.isEmpty();
	}

	/**
	 * @param list
	 * @return boolean
	 */
	public static boolean isNotEmpty(List<?> list) {
		return !isEmpty(list);
	}

	/**
	 * @param list
	 * @param index
	 * @return boolean
	 */
	public static boolean isEmpty(List<?> list, int index) {
		return list == null || list.get(index) == null;
	}

	/**
	 * @param list
	 * @param index
	 * @return boolean
	 */
	public static boolean isNotEmpty(List<?> list, int index) {
		return !isEmpty(list, index);
	}

	/**
	 * @param map
	 * @param key
	 * @return boolean
	 */
	@SuppressWarnings("unchecked")
	public static List<String> getRequestArray(Map<String, ?> map, String key) {

		ArrayList<String> ret = new ArrayList<String>();
		if (map.get(key) instanceof ArrayList) {
			ret = (ArrayList<String>) map.get(key);
		}
		if (map.get(key) instanceof String) {
			ret.add((String) map.get(key));
		}
		return ret;
	}

	/**
	 * @param <M>
	 * @param modelClass
	 * @param key
	 * @return keyList
	 */
	public static <M> List<Key> gatKeyList(ModelMeta<M> modelClass, Key key) {
		if (key == null) {
			return new ArrayList<Key>();
		}
		return Datastore.query(modelClass, key).asKeyList();
	}

	/**
	 * URLのコネクションを返す。
	 * 
	 * @param baseUrl
	 * @param param
	 * @param method
	 * @return HttpURLConnection
	 * @throws IOException
	 */
	public static HttpURLConnection getURLConnection(String baseUrl, String param, String method) throws IOException {
		URL url = new URL(baseUrl);
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		connection.setDoOutput(true);
		connection.setRequestMethod(method);
		OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
		writer.write(param);
		writer.close();
		return connection;
	}

	/**
	 * パーセンテージ取得
	 * 
	 * @param num1
	 * @param num2
	 * @return
	 */
	public static double getPercent(long num1, long num2) {
		if (num1 == 0 || num2 == 0) {
			return 0;
		}

		BigDecimal dec1 = new BigDecimal(num1);
		BigDecimal dec2 = new BigDecimal(num2);

		double ret = dec1.divide(dec2, 4, BigDecimal.ROUND_DOWN).multiply(new BigDecimal(100)).doubleValue();

		return ret;
	}

	/**
	 * 発走時間を超えているかどうか
	 * 
	 * @param raceInfo
	 * @return true:超えている
	 */
	public static boolean isStartTimeOver(RaceInfo raceInfo) {
		if (raceInfo == null || raceInfo.getStartTime() == null) {
			return false;
		}

		if (raceInfo.getProgress() == BurubakaConstants.PROGRESS_CLOSING_EXTEND) {
			return false;
		}

		Calendar now = getCalendarInstance();
		Calendar stlien = getCalendarInstance();
		stlien.setTime(raceInfo.getStartTime());

		return now.compareTo(stlien) >= 0;
	}

	/**
	 * 投票受付前かどうか
	 * 
	 * @param raceInfo
	 * @return true:投票受付前
	 */
	public static boolean isBeforeBetting(RaceInfo raceInfo) {
		return raceInfo == null || isBeforeBetting(raceInfo.getProgress());
	}

	/**
	 * 投票受付前かどうか
	 * 
	 * @param raceInfo
	 * @return true:投票受付前
	 */
	public static boolean isBeforeBetting(int progress) {
		return progress == BurubakaConstants.PROGRESS_NOMINATION;
	}

	/**
	 * 投票受付期間中かどうか
	 * 
	 * @param raceInfo
	 * @return true:投票受付期間中
	 */
	public static boolean isWithinBetting(RaceInfo raceInfo) {
		if (raceInfo == null || raceInfo.getDeadline() == null) {
			return false;
		}
		return isWithinBetting(raceInfo.getProgress(), raceInfo.getDeadline());
	}

	/**
	 * 投票受付期間中かどうか
	 * 
	 * @param raceInfo
	 * @return true:投票受付期間中
	 */
	public static boolean isWithinBetting(int progress, Date deadline) {
		if (progress < BurubakaConstants.PROGRESS_RACECARD) {
			return false;
		}
		if (isOverDeadline(progress, deadline)) {
			return false;
		}
		return true;
	}

	/**
	 * 締め切り時間を超えているかどうか
	 * 
	 * @param raceInfo
	 * @return true:超えている
	 */
	public static boolean isOverDeadline(RaceInfo raceInfo) {
		if (raceInfo == null || raceInfo.getDeadline() == null) {
			return false;
		}
		return isOverDeadline(raceInfo.getProgress(), raceInfo.getDeadline());
	}

	/**
	 * 締め切り時間を超えているかどうか
	 * 
	 * @param raceInfo
	 * @return true:超えている
	 */
	public static boolean isOverDeadline(int progress, Date deadline) {
		if (deadline == null) {
			return false;
		}

		if (progress == BurubakaConstants.PROGRESS_CLOSING_EXTEND) {
			return false;
		}

		if (progress >= BurubakaConstants.PROGRESS_RACERESULT) {
			return true;
		}

		Calendar now = getCalendarInstance();
		Calendar dline = getCalendarInstance();
		dline.setTime(deadline);

		return now.compareTo(dline) >= 0;
	}

	/**
	 * 本登録が行えるかどうか
	 * 
	 * @param raceInfo
	 * @return true:行える
	 */
	public static boolean isDoRaceCard(RaceInfo raceInfo) {
		if (raceInfo == null) {
			return false;
		}
		return isDoRaceCard(raceInfo.getProgress(), raceInfo.getStartTime());
	}

	/**
	 * 本登録が行えるかどうか
	 * 
	 * @param progress
	 * @param startDate
	 * @return true:行える
	 */
	public static boolean isDoRaceCard(int progress, Date startDate) {
		if (startDate == null) {
			return false;
		}

		if (progress > BurubakaConstants.PROGRESS_NOMINATION) {
			return false;
		}

		Calendar now = getCalendarInstance();
		Calendar start = cluculateDate(startDate, -2, 0, 0, 0);

		return now.compareTo(start) >= 0;
	}

	/**
	 * カレンダーインスタンス取得
	 * 
	 * @return Calendar
	 */
	public static Calendar getCalendarInstance() {
		return GregorianCalendar.getInstance(TimeZoneLocator.get());
	}

	/**
	 * 地方かどうか
	 * 
	 * @param distClass
	 * @return true:地方
	 */
	public static boolean isLocal(String distClass) {
		return BurubakaConstants.DISTRICT_CLASS_LOCAL.equals(distClass);
	}

	/**
	 * aタグ除去
	 * 
	 * @param horseNamesA
	 * @return
	 */
	public static ArrayList<String> removeATag(ArrayList<String> horseNamesA) {
		return BurubakaUtil.substringList("'>", "</a>", horseNamesA.toString());
	}

	/**
	 * aタグ除去
	 * 
	 * @param horseNameA
	 * @return
	 */
	public static String removeATag(String horseNameA) {
		return BurubakaUtil.substring(">", "</a>", horseNameA);
	}

	/**
	 * 配当文字列をlong型にパースする
	 * 
	 * @param haitou
	 * @return long
	 */
	public static long parserLongHaitou(String haitou) {
		return (Long.parseLong(haitou.replaceAll("円", "").replaceAll(",", "")));
	}

	/**
	 * 締切り日を取得する
	 * 
	 * @param text
	 * @param url
	 * @return RaceInfo
	 * @throws Exception
	 */
	public static Date getDeadline(Date start) throws Exception {
		// レース情報取得
		Calendar calendar = GregorianCalendar.getInstance();
		calendar.setTime(start);
		calendar.add(Calendar.MINUTE, BurubakaConstants.DIFFERENCE_CLOSE_START_MINUTE);
		return calendar.getTime();
	}

	/**
	 * 文字列をUTF-8でURLencodeする
	 * 
	 * @param str
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String urlEncodeUTF8(String str) throws UnsupportedEncodingException {
		return URLEncoder.encode(str, BurubakaConstants.ENCODE_UTF8);
	}

	/**
	 * 文字列をShift-JisでURLencodeする
	 * 
	 * @param str
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static String urlEncodeSjis(String str) throws UnsupportedEncodingException {
		return URLEncoder.encode(str, BurubakaConstants.ENCODE_SJIS);
	}

	/**
	 * 日付の計算を行う
	 * 
	 * @param date
	 *            基準日
	 * @param day
	 *            日
	 * @param hour
	 *            時間
	 * @param min
	 *            分
	 * @param sec
	 *            秒
	 * @return
	 */
	public static Calendar cluculateDate(Date date, int day, int hour, int min, int sec) {
		Calendar cal = getCalendarInstance();
		cal.setTime(date);
		cal.add(Calendar.DATE, day);
		cal.add(Calendar.HOUR, hour);
		cal.add(Calendar.MINUTE, min);
		cal.add(Calendar.SECOND, sec);
		return cal;
	}

	/**
	 * たて文字を取得する
	 * 
	 * @return
	 */
	public static String getVerticalString(String str) {

		StringBuffer sb = new StringBuffer();
		String temp = str.replaceAll("ー", "|").replaceAll("-", "|");
		char[] array = temp.toCharArray();
		for (char c : array) {
			sb.append(c);
			sb.append("<br>");
		}
		return sb.toString();
	}

	// /**
	// * 競馬場から競馬場Noを取得する
	// *
	// * @param raceCourse
	// * @return racecourseNo
	// */
	// public static String getRacecourseNo(String raceCourse) {
	// if (raceCourse == null) {
	// return null;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_SAPPORO) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_SAPPORO;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_HAKODATE) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_HAKODATE;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_FUKUSHIMA) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_FUKUSHIMA;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_TOKYO) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_TOKYO;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_NAKAYAMA) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_NAKAYAMA;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_CHUKYO) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_CHUKYO;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_KYOTO) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_KYOTO;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_HANSHIN) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_HANSHIN;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_KOKURA) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_KOKURA;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_ASAHIKAWA) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_ASAHIKAWA;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_KYUKA) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_KYUKA;
	// }
	//
	// if (raceCourse.indexOf(BurubakaConstants.RACECOURSE_MONBETSU) >= 0) {
	// return BurubakaConstants.RACECOURSE_NO_MONBETSU;
	// }
	//
	// return null;
	// }
	//
	// /**
	// * 文字列からハンディを取得する
	// *
	// * @param text
	// * @return Handi
	// */
	// public static String getHandi(String text) {
	//
	// if (text.indexOf(BurubakaConstants.HANDI_TEIRYO) >= 0) {
	// return BurubakaConstants.HANDI_TEIRYO;
	// }
	//
	// if (text.indexOf(BurubakaConstants.HANDI_BAREI) >= 0) {
	// return BurubakaConstants.HANDI_BAREI;
	// }
	//
	// if (text.indexOf(BurubakaConstants.HANDI_BETEI) >= 0) {
	// return BurubakaConstants.HANDI_BETEI;
	// }
	//
	// if (text.indexOf(BurubakaConstants.HANDI_HANDI) >= 0) {
	// return BurubakaConstants.HANDI_HANDI;
	// }
	//
	// if (text.indexOf(BurubakaConstants.HANDI_GBETEI) >= 0) {
	// return BurubakaConstants.HANDI_GBETEI;
	// }
	//
	// return "";
	// }

}
